/*
 * View.java
 */

package downloadmanager;

import java.util.Observable;
import javax.swing.event.ListSelectionEvent;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;
import java.util.Observer;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionListener;
import org.jdesktop.application.Application;

/**
 * The application's main frame.
 */
public class View extends FrameView implements Observer {

    private int _selectedRow = -1;
    
    public View(SingleFrameApplication app) {
        super(app);
        initComponents();

        
        // MainFrame Title
        getFrame().setTitle("Quick Download Manager");
        
        // MainFrame Exit Listener
        getApplication().addExitListener(new ConfirmExit());
        
        // View Preferences
        if (getApp().getPreferencesModel().get("tabDetails") != null)
        {
            if (getApp().getPreferencesModel().get("tabDetails").equals("0"))
            {
                buttonDetails.setSelected(false);
                tabDetails.setVisible(false);
            }
        }
        
        // Table Files select row change
        tableFiles.setModel(getApp().getDownloadsModel());
        tableFiles.getColumnModel().getColumn(1).setMaxWidth(100);
        tableFiles.getColumnModel().getColumn(2).setMaxWidth(100);
        tableFiles.getColumnModel().getColumn(3).setMaxWidth(100);
        tableFiles.getColumnModel().getColumn(4).setMaxWidth(140);
        tableFiles.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        tableFiles.getSelectionModel().addListSelectionListener(
            new ListSelectionListener() {
                public void valueChanged(ListSelectionEvent e) {
                    updateSelection(tableFiles.getSelectedRow());
                }});
                
        // Progress Bar Render
        ProgressBar pb = new ProgressBar();
        pb.setStringPainted(true);
        tableFiles.getColumnModel().getColumn(1).setCellRenderer(pb);
        
        // Status bar initialization (2)
        statusMessageLabel.setText(getApp().getDownloadsModel().getRowCount() + " Downloads");
        
        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText("");
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        statusBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    statusBar.setVisible(true);
                    statusBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    statusBar.setVisible(false);
                    statusBar.setValue(0);
                } else if ("message".equals(propertyName)) {
                    String text = (String)(evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? "" : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer)(evt.getNewValue());
                    statusBar.setVisible(true);
                    statusBar.setIndeterminate(false);
                    statusBar.setValue(value);
                }
            }
        });
    }
    
    public void init() {
    }

    @Action public void toggleTabDetails() {
        getApp().getPreferencesModel().add("tabDetails", buttonDetails.isSelected() ? "1" : "0");
        tabDetails.setVisible(buttonDetails.isSelected());
    }

    @Action public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = App.getApplication().getMainFrame();
            aboutBox = new AboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        App.getApplication().show(aboutBox);
    }

    @Action public void showPreferencesBox() {
        if (preferencesBox == null) {
            JFrame mainFrame = App.getApplication().getMainFrame();
            preferencesBox = new PreferencesBox(mainFrame);
            preferencesBox.setLocationRelativeTo(mainFrame);
        }
        App.getApplication().show(preferencesBox);
    }

    @Action public void showDownloadAddDialog() {
        
        String s = getApp().getPreferencesModel().get("defaultFolder");
        
        if (s == null || s.equals(""))
        {
            JOptionPane.showMessageDialog(getFrame(), "Tem de definir uma pasta de downloads.", "Alerta", JOptionPane.WARNING_MESSAGE);
            showPreferencesBox();
            return;
        }
        
        if (downloadAddDialog == null) {
            JFrame mainFrame = App.getApplication().getMainFrame();
            downloadAddDialog = new DownloadAddBox(mainFrame);
            downloadAddDialog.setLocationRelativeTo(mainFrame);
        }
        App.getApplication().show(downloadAddDialog);
    }

    @Action public void downloadStart() {
        if (_selectedRow == -1)
        {
            JOptionPane.showMessageDialog(getFrame(), "Tem de selecionar um ficheiro.");
        }
        else
        {
            getApp().getDownloadsModel().get(_selectedRow).download();
            updateButtons();
        }
    }

    @Action public void downloadStop() {
        if (_selectedRow == -1)
        {
            JOptionPane.showMessageDialog(getFrame(), "Tem de selecionar um ficheiro.");
        }
        else
        {
            getApp().getDownloadsModel().get(_selectedRow).stop();
            updateButtons();
        }
    }

    @Action public void downloadDelete() {
        if (_selectedRow == -1)
        {
            JOptionPane.showMessageDialog(getFrame(), "Tem de selecionar um ficheiro.");
        }
        else
        {
            getApp().getDownloadsModel().delete(_selectedRow);
        }
    }

    /**
     * Events that triggers when the selected row changes state.
     * Observer is managed by updateSelection.
     * 
     * @param o
     * @param arg
     */
    public void update(Observable o, Object arg) {
        updateButtons();
    }

    public void updateSelection(int row) {
        // Remove Observer from last selected download and
        // start Observing new selected download
        if (_selectedRow != -1)
        {
            Download d = getApp().getDownloadsModel().get(_selectedRow);
            if (d != null)
            {
                d.deleteObserver(this);
            }
        }
        
        if (row != -1)
        {
            getApp().getDownloadsModel().get(row).addObserver(this);
        }

        // Update selected row
        _selectedRow = row;

        // Perform updates
        updateButtons();
        updateDetails();
    }
    
    public void updateButtons()
    {
        if (_selectedRow == -1)
        {
            buttonDelete.setEnabled(false);
            buttonPlay.setEnabled(false);
            buttonStop.setEnabled(false);
        }
        else
        {
            Download f = getApp().getDownloadsModel().get(_selectedRow);

            buttonDelete.setEnabled(true);
            buttonPlay.setEnabled(
                f.getTheState() == Download.State.ERROR ||
                f.getTheState() == Download.State.BEGIN ||
                f.getTheState() == Download.State.STOPPED);

            buttonStop.setEnabled(
                f.getTheState() == Download.State.ACTIVE ||
                f.getTheState() == Download.State.CONNECTING);
        }
    }
    
    public void updateDetails()
    {
        if (_selectedRow == -1)
        {
            return;
        }

        Download f = getApp().getDownloadsModel().get(_selectedRow);

        labelDetailsURL.setText(f.getSource().toString());
        labelDetailsDestination.setText(f.getDestination().getAbsolutePath());
    }

    private class ConfirmExit implements Application.ExitListener {
        public boolean canExit(EventObject e)
        {
            return 0 == JOptionPane.showConfirmDialog(getFrame(), "Deseja mesmo sair?", "Sair?", JOptionPane.YES_NO_OPTION);
        }
        
        /**
         * Called before exiting
         */
        public void willExit(EventObject e) {
            // Stop all active downloads
            getApp().getDownloadsModel().stopActive();
            getApp().saveApplication();
        }
    }

    private App getApp() {
        return App.getApplication();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        toolbar = new javax.swing.JToolBar();
        buttonAdd = new javax.swing.JButton();
        buttonDelete = new javax.swing.JButton();
        jSeparator1 = new javax.swing.JToolBar.Separator();
        buttonPlay = new javax.swing.JButton();
        buttonStop = new javax.swing.JButton();
        buttonDetails = new javax.swing.JToggleButton();
        jScrollPane2 = new javax.swing.JScrollPane();
        tableFiles = new javax.swing.JTable();
        tabDetails = new javax.swing.JTabbedPane();
        jPanel1 = new javax.swing.JPanel();
        labelDetailsJ1 = new javax.swing.JLabel();
        labelDetailsURL = new javax.swing.JLabel();
        labelDetailsJ2 = new javax.swing.JLabel();
        labelDetailsDestination = new javax.swing.JLabel();
        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();
        jSeparator2 = new javax.swing.JSeparator();
        javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        statusBar = new javax.swing.JProgressBar();

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(downloadmanager.App.class).getContext().getResourceMap(View.class);
        mainPanel.setToolTipText(resourceMap.getString("mainPanel.toolTipText")); // NOI18N
        mainPanel.setMaximumSize(new java.awt.Dimension(999999, 999999));
        mainPanel.setMinimumSize(new java.awt.Dimension(760, 400));
        mainPanel.setName("mainPanel"); // NOI18N

        toolbar.setFloatable(false);
        toolbar.setRollover(true);
        toolbar.setName("toolbar"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(downloadmanager.App.class).getContext().getActionMap(View.class, this);
        buttonAdd.setAction(actionMap.get("showDownloadAddDialog")); // NOI18N
        buttonAdd.setFocusable(false);
        buttonAdd.setHideActionText(true);
        buttonAdd.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        buttonAdd.setName("buttonAdd"); // NOI18N
        buttonAdd.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(buttonAdd);

        buttonDelete.setAction(actionMap.get("downloadDelete")); // NOI18N
        buttonDelete.setEnabled(false);
        buttonDelete.setFocusable(false);
        buttonDelete.setHideActionText(true);
        buttonDelete.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        buttonDelete.setName("buttonDelete"); // NOI18N
        buttonDelete.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(buttonDelete);

        jSeparator1.setName("jSeparator1"); // NOI18N
        toolbar.add(jSeparator1);

        buttonPlay.setAction(actionMap.get("downloadStart")); // NOI18N
        buttonPlay.setEnabled(false);
        buttonPlay.setFocusable(false);
        buttonPlay.setHideActionText(true);
        buttonPlay.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        buttonPlay.setName("buttonPlay"); // NOI18N
        buttonPlay.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(buttonPlay);

        buttonStop.setAction(actionMap.get("downloadStop")); // NOI18N
        buttonStop.setEnabled(false);
        buttonStop.setFocusable(false);
        buttonStop.setHideActionText(true);
        buttonStop.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        buttonStop.setName("buttonStop"); // NOI18N
        buttonStop.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(buttonStop);

        buttonDetails.setAction(actionMap.get("toggleTabDetails")); // NOI18N
        buttonDetails.setSelected(true);
        buttonDetails.setFocusable(false);
        buttonDetails.setHideActionText(true);
        buttonDetails.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        buttonDetails.setName("buttonDetails"); // NOI18N
        buttonDetails.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toolbar.add(buttonDetails);

        jScrollPane2.setBackground(resourceMap.getColor("jScrollPane2.background")); // NOI18N
        jScrollPane2.setFocusCycleRoot(true);
        jScrollPane2.setName("jScrollPane2"); // NOI18N

        tableFiles.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        tableFiles.setMinimumSize(new java.awt.Dimension(60, 150));
        tableFiles.setName("tableFiles"); // NOI18N
        tableFiles.setRowHeight(18);
        jScrollPane2.setViewportView(tableFiles);

        tabDetails.setTabLayoutPolicy(javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT);
        tabDetails.setName("tabDetails"); // NOI18N

        jPanel1.setName("jPanel1"); // NOI18N

        labelDetailsJ1.setText(resourceMap.getString("labelDetailsJ1.text")); // NOI18N
        labelDetailsJ1.setName("labelDetailsJ1"); // NOI18N

        labelDetailsURL.setText(resourceMap.getString("labelDetailsURL.text")); // NOI18N
        labelDetailsURL.setName("labelDetailsURL"); // NOI18N

        labelDetailsJ2.setText(resourceMap.getString("labelDetailsJ2.text")); // NOI18N
        labelDetailsJ2.setName("labelDetailsJ2"); // NOI18N

        labelDetailsDestination.setText(resourceMap.getString("labelDetailsDestination.text")); // NOI18N
        labelDetailsDestination.setName("labelDetailsDestination"); // NOI18N

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(labelDetailsJ1, javax.swing.GroupLayout.PREFERRED_SIZE, 78, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(labelDetailsJ2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(labelDetailsDestination)
                    .addComponent(labelDetailsURL, javax.swing.GroupLayout.DEFAULT_SIZE, 651, Short.MAX_VALUE))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(labelDetailsJ1)
                    .addComponent(labelDetailsURL))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(labelDetailsJ2)
                    .addComponent(labelDetailsDestination))
                .addContainerGap(36, Short.MAX_VALUE))
        );

        tabDetails.addTab(resourceMap.getString("jPanel1.TabConstraints.tabTitle"), jPanel1); // NOI18N

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(toolbar, javax.swing.GroupLayout.DEFAULT_SIZE, 760, Short.MAX_VALUE)
            .addComponent(tabDetails, javax.swing.GroupLayout.DEFAULT_SIZE, 760, Short.MAX_VALUE)
            .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 760, Short.MAX_VALUE)
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(mainPanelLayout.createSequentialGroup()
                .addComponent(toolbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(7, 7, 7)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(tabDetails, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        menuBar.setName("menuBar"); // NOI18N

        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N

        jMenuItem1.setAction(actionMap.get("showPreferencesBox")); // NOI18N
        jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_P, java.awt.event.InputEvent.CTRL_MASK));
        jMenuItem1.setName("jMenuItem1"); // NOI18N
        fileMenu.add(jMenuItem1);

        jSeparator2.setName("jSeparator2"); // NOI18N
        fileMenu.add(jSeparator2);

        exitMenuItem.setAction(actionMap.get("quit")); // NOI18N
        exitMenuItem.setName("exitMenuItem"); // NOI18N
        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        statusPanel.setName("statusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        statusBar.setName("statusBar"); // NOI18N

        javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusPanelSeparator, javax.swing.GroupLayout.DEFAULT_SIZE, 760, Short.MAX_VALUE)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(statusMessageLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 590, Short.MAX_VALUE)
                .addComponent(statusBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusAnimationLabel)
                .addContainerGap())
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addComponent(statusPanelSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(statusMessageLabel)
                    .addComponent(statusAnimationLabel)
                    .addComponent(statusBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(3, 3, 3))
        );

        setComponent(mainPanel);
        setMenuBar(menuBar);
        setStatusBar(statusPanel);
        setToolBar(toolbar);
    }// </editor-fold>//GEN-END:initComponents

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton buttonAdd;
    private javax.swing.JButton buttonDelete;
    private javax.swing.JToggleButton buttonDetails;
    private javax.swing.JButton buttonPlay;
    private javax.swing.JButton buttonStop;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JToolBar.Separator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JLabel labelDetailsDestination;
    private javax.swing.JLabel labelDetailsJ1;
    private javax.swing.JLabel labelDetailsJ2;
    private javax.swing.JLabel labelDetailsURL;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JProgressBar statusBar;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    private javax.swing.JTabbedPane tabDetails;
    private javax.swing.JTable tableFiles;
    private javax.swing.JToolBar toolbar;
    // End of variables declaration//GEN-END:variables

    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;

    private JDialog aboutBox;
    private JDialog preferencesBox;
    private JDialog downloadAddDialog;
}